Next.jsããã«ãŠã§ã¢ã䜿çšããé«åºŠãªãªã¯ãšã¹ã倿Žãã¯ããã¯ã«ã€ããŠèª¬æããŸããå ç¢ãªWebã¢ããªã±ãŒã·ã§ã³ã®ããã«ãè€éãªã«ãŒãã£ã³ã°ãèªèšŒãA/Bãã¹ããããŒã«ãªãŒãŒã·ã§ã³æŠç¥ãåŠçããæ¹æ³ãåŠã³ãŸãã
Next.jsããã«ãŠã§ã¢ã®ãšããžã±ãŒã¹ïŒãªã¯ãšã¹ã倿Žãã¿ãŒã³ããã¹ã¿ãŒãã
Next.jsããã«ãŠã§ã¢ã¯ãã¢ããªã±ãŒã·ã§ã³ã®ã«ãŒãã«å°éããåã«ãªã¯ãšã¹ããã€ã³ã¿ãŒã»ããããŠå€æŽããããã®åŒ·åãªã¡ã«ããºã ãæäŸããŸãããã®æ©èœã«ãããç°¡åãªèªèšŒãã§ãã¯ãããè€éãªA/Bãã¹ãã·ããªãªãåœéåæŠç¥ãŸã§ãå¹ åºãå¯èœæ§ãéãããŸãããã ããããã«ãŠã§ã¢ã广çã«æŽ»çšããã«ã¯ããã®ãšããžã±ãŒã¹ãšæœåšçãªèœãšãç©Žãæ·±ãçè§£ããå¿ èŠããããŸãããã®å æ¬çãªã¬ã€ãã§ã¯ãé«åºŠãªãªã¯ãšã¹ã倿Žãã¿ãŒã³ãæ€èšããå ç¢ã§ããã©ãŒãã³ã¹ã®é«ãNext.jsã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããã®ã«åœ¹ç«ã€å®è·µçãªäŸãšå®çšçãªæŽå¯ãæäŸããŸãã
Next.jsããã«ãŠã§ã¢ã®åºç€ãçè§£ãã
é«åºŠãªãã¿ãŒã³ã«å ¥ãåã«ãNext.jsããã«ãŠã§ã¢ã®åºæ¬ã埩ç¿ããŸããããããã«ãŠã§ã¢é¢æ°ã¯ããªã¯ãšã¹ããå®äºããåã«å®è¡ãããæ¬¡ã®ããšãå¯èœã«ãªããŸãã
- URLã®æžãæãïŒç¹å®ã®åºæºã«åºã¥ããŠãŠãŒã¶ãŒãç°ãªãããŒãžã«ãªãã€ã¬ã¯ãããŸãã
- ãŠãŒã¶ãŒã®ãªãã€ã¬ã¯ãïŒèªèšŒãŸãã¯æ¿èªã®ç®çã§ããŠãŒã¶ãŒãå®å šã«ç°ãªãURLã«éä¿¡ããŸãã
- ããããŒã®å€æŽïŒHTTPããããŒã远å ãåé€ããŸãã¯æŽæ°ããŸãã
- çŽæ¥å¿çïŒNext.jsã«ãŒãããã€ãã¹ããŠãããã«ãŠã§ã¢ããçŽæ¥å¿çãè¿ããŸãã
ããã«ãŠã§ã¢é¢æ°ã¯ãmiddleware.js
ãŸãã¯middleware.ts
ãã¡ã€ã«å
ã®/pages
ãŸãã¯/app
ãã£ã¬ã¯ããªã«ååšããŸãïŒNext.jsã®ããŒãžã§ã³ãšã»ããã¢ããã«ãã£ãŠç°ãªããŸãïŒããããã¯ãåä¿¡ãªã¯ãšã¹ãã衚ãNextRequest
ãªããžã§ã¯ããåãåããåŸç¶ã®åäœãå¶åŸ¡ããããã«NextResponse
ãªããžã§ã¯ããè¿ãããšãã§ããŸãã
äŸïŒåºæ¬çãªèªèšŒããã«ãŠã§ã¢
ãã®äŸã¯ãç°¡åãªèªèšŒãã§ãã¯ã瀺ããŠããŸãããŠãŒã¶ãŒãèªèšŒãããŠããªãå ŽåïŒããšãã°ãCookieã«æå¹ãªããŒã¯ã³ããªãå ŽåïŒããã°ã€ã³ããŒãžã«ãªãã€ã¬ã¯ããããŸãã
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const authToken = request.cookies.get('authToken')
if (!authToken) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/protected/:path*'],
}
ãã®ããã«ãŠã§ã¢ã¯ã/protected/:path*
ã«äžèŽããã«ãŒãã«å¯ŸããŠã®ã¿å®è¡ãããŸããauthToken
Cookieã®ååšã確èªããŸãã CookieãèŠã€ãããªãå ŽåããŠãŒã¶ãŒã¯/login
ããŒãžã«ãªãã€ã¬ã¯ããããŸãããã以å€ã®å ŽåãNextResponse.next()
ã䜿çšããŠãªã¯ãšã¹ãã¯éåžžã©ããç¶è¡ã§ããŸãã
é«åºŠãªãªã¯ãšã¹ã倿Žãã¿ãŒã³
次ã«ãNext.jsããã«ãŠã§ã¢ã®çã®ãã¯ãŒã瀺ããé«åºŠãªãªã¯ãšã¹ã倿Žãã¿ãŒã³ãããã€ãèŠãŠã¿ãŸãããã
1. Cookieã䜿çšããA/Bãã¹ã
A/Bãã¹ãã¯ããŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ãæé©åããããã®éèŠãªææ³ã§ããããã«ãŠã§ã¢ã䜿çšããŠããŠãŒã¶ãŒãã¢ããªã±ãŒã·ã§ã³ã®ããŸããŸãªããªãšãŒã·ã§ã³ã«ã©ã³ãã ã«å²ãåœãŠããã®åäœã远跡ã§ããŸãããã®ãã¿ãŒã³ã¯ãCookieã䜿çšããŠãŠãŒã¶ãŒã®å²ãåœãŠãããããªã¢ã³ããä¿æããŸãã
äŸïŒã©ã³ãã£ã³ã°ããŒãžã®A/Bãã¹ã
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const VARIANT_A = 'variantA'
const VARIANT_B = 'variantB'
export function middleware(request: NextRequest) {
let variant = request.cookies.get('variant')?.value
if (!variant) {
// Randomly assign a variant
variant = Math.random() < 0.5 ? VARIANT_A : VARIANT_B
const response = NextResponse.next()
response.cookies.set('variant', variant)
return response
}
if (variant === VARIANT_A) {
return NextResponse.rewrite(new URL('/variant-a', request.url))
} else if (variant === VARIANT_B) {
return NextResponse.rewrite(new URL('/variant-b', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/'],
}
ãã®äŸã§ã¯ããŠãŒã¶ãŒãã«ãŒããã¹ïŒ/
ïŒã«åããŠã¢ã¯ã»ã¹ãããšãããã«ãŠã§ã¢ã¯ããããvariantA
ãŸãã¯variantB
ã®ããããã«ã©ã³ãã ã«å²ãåœãŠãŸãããã®ããªã¢ã³ãã¯Cookieã«ä¿åãããŸããåããŠãŒã¶ãŒããã®åŸç¶ã®ãªã¯ãšã¹ãã¯ãå²ãåœãŠãããããªã¢ã³ãã«å¿ããŠã/variant-a
ãŸãã¯/variant-b
ã«æžãæããããŸããããã«ãããç°ãªãã©ã³ãã£ã³ã°ããŒãžãæäŸããã©ã¡ãã®ããã©ãŒãã³ã¹ãåªããŠãããã远跡ã§ããŸãã Next.jsã¢ããªã±ãŒã·ã§ã³ã§/variant-a
ããã³/variant-b
ã®ã«ãŒããå®çŸ©ãããŠããããšã確èªããŠãã ããã
ã°ããŒãã«ãªèæ ®äºé ïŒ A/Bãã¹ããå®è¡ããå Žåã¯ãå°åããšã®ããªãšãŒã·ã§ã³ãèæ ®ããŠãã ãããåç±³ã§å ±æãåŒã¶ãã¶ã€ã³ã¯ãã¢ãžã¢ã§ã¯å¹æããªãå¯èœæ§ããããŸããïŒIPã¢ãã¬ã¹ã®æ€çŽ¢ãŸãã¯ãŠãŒã¶ãŒèšå®ãéããŠååŸããïŒãžãªãã±ãŒã·ã§ã³ããŒã¿ã䜿çšããŠãA/Bãã¹ããç¹å®ã®å°åã«åãããããšãã§ããŸãã
2. URLæžãæãã«ããããŒã«ãªãŒãŒã·ã§ã³ïŒi18nïŒ
åœéåïŒi18nïŒã¯ãã°ããŒãã«ãªãªãŒãã£ãšã³ã¹ã«ãªãŒãããããã«äžå¯æ¬ ã§ããããã«ãŠã§ã¢ã䜿çšããŠããŠãŒã¶ãŒã®åªå èšèªãèªåçã«æ€åºãããµã€ãã®é©åãªããŒã«ã©ã€ãºãããããŒãžã§ã³ã«ãªãã€ã¬ã¯ãã§ããŸãã
äŸïŒ`Accept-Language`ããããŒã«åºã¥ããŠãªãã€ã¬ã¯ããã
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const SUPPORTED_LANGUAGES = ['en', 'fr', 'es', 'de']
const DEFAULT_LANGUAGE = 'en'
function getPreferredLanguage(request: NextRequest): string {
const acceptLanguage = request.headers.get('accept-language')
if (!acceptLanguage) {
return DEFAULT_LANGUAGE
}
const languages = acceptLanguage.split(',').map((lang) => lang.split(';')[0].trim())
for (const lang of languages) {
if (SUPPORTED_LANGUAGES.includes(lang)) {
return lang
}
}
return DEFAULT_LANGUAGE
}
export function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname
// Check if there's an existing locale in the pathname
if (
SUPPORTED_LANGUAGES.some(
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
)
) {
return NextResponse.next()
}
const preferredLanguage = getPreferredLanguage(request)
return NextResponse.redirect(
new URL(`/${preferredLanguage}${pathname}`, request.url)
)
}
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico).*)'
],
}
ãã®ããã«ãŠã§ã¢ã¯ããªã¯ãšã¹ãããAccept-Language
ããããŒãæœåºãããŠãŒã¶ãŒã®åªå
èšèªã決å®ããŸãã URLã«èšèªãã¬ãã£ãã¯ã¹ïŒäŸïŒ/en/about
ïŒããŸã å«ãŸããŠããªãå Žåãããã«ãŠã§ã¢ã¯ãŠãŒã¶ãŒãé©åãªããŒã«ã©ã€ãºãããURLïŒäŸïŒãã©ã³ã¹èªã®å Žåã¯/fr/about
ïŒã«ãªãã€ã¬ã¯ãããŸãã `/pages`ãŸãã¯`/app`ãã£ã¬ã¯ããªã«ãç°ãªããã±ãŒã«ã«å¯Ÿå¿ããé©åãªãã©ã«ããŒæ§é ãããããšã確èªããŠãã ãããããšãã°ã`/pages/en/about.js`ãã¡ã€ã«ãš`/pages/fr/about.js`ãã¡ã€ã«ãå¿
èŠã«ãªããŸãã
ã°ããŒãã«ãªèæ ®äºé ïŒ i18nã®å®è£ ã§ãå³ããå·Šã«èšè¿°ããèšèªïŒäŸïŒã¢ã©ãã¢èªãããã©ã€èªïŒãæ£ããåŠçãããããšã確èªããŠãã ããããŸããã³ã³ãã³ãé ä¿¡ãããã¯ãŒã¯ïŒCDNïŒã䜿çšããŠãããŒã«ã©ã€ãºãããã¢ã»ããããŠãŒã¶ãŒã«è¿ããµãŒããŒããæäŸããããã©ãŒãã³ã¹ãåäžãããããšãæ€èšããŠãã ããã
3. æ©èœãã©ã°
æ©èœãã©ã°ã䜿çšãããšãæ°ããã³ãŒãããããã€ããã«ãã¢ããªã±ãŒã·ã§ã³ã®æ©èœãæå¹ãŸãã¯ç¡å¹ã«ã§ããŸããããã¯ãæ°ããæ©èœã段éçã«å±éããããæ¬çªç°å¢ã§æ©èœããã¹ããããããå Žåã«ç¹ã«äŸ¿å©ã§ããããã«ãŠã§ã¢ã䜿çšããŠãæ©èœãã©ã°ã®ã¹ããŒã¿ã¹ã確èªããããã«å¿ããŠãªã¯ãšã¹ãã倿Žã§ããŸãã
äŸïŒããŒã¿æ©èœãæå¹ã«ãã
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const BETA_FEATURE_ENABLED = process.env.BETA_FEATURE_ENABLED === 'true'
export function middleware(request: NextRequest) {
if (BETA_FEATURE_ENABLED && request.nextUrl.pathname.startsWith('/new-feature')) {
return NextResponse.next()
}
// Optionally redirect to a "feature unavailable" page
return NextResponse.rewrite(new URL('/feature-unavailable', request.url))
}
export const config = {
matcher: ['/new-feature/:path*'],
}
ãã®ããã«ãŠã§ã¢ã¯ãBETA_FEATURE_ENABLED
ç°å¢å€æ°ã®å€ã確èªããŸãã true
ã«èšå®ãããŠããŠããŠãŒã¶ãŒã/new-feature
ã®ã«ãŒãã«ã¢ã¯ã»ã¹ããããšããŠããå Žåããªã¯ãšã¹ãã¯ç¶è¡ã§ããŸãããã以å€ã®å ŽåããŠãŒã¶ãŒã¯/feature-unavailable
ããŒãžã«ãªãã€ã¬ã¯ããããŸããããŸããŸãªç°å¢ïŒéçºãã¹ããŒãžã³ã°ãæ¬çªïŒã«åãããŠç°å¢å€æ°ãé©åã«æ§æããããšãå¿ããªãã§ãã ããã
ã°ããŒãã«ãªèæ ®äºé ïŒæ©èœãã©ã°ã䜿çšããå Žåã¯ããã¹ãŠã®å°åã®èŠå¶ã«æºæ ããŠããªãå¯èœæ§ã®ããæ©èœãæå¹ã«ããæ³ç圱é¿ãèæ ®ããŠãã ãããããšãã°ãããŒã¿ãã©ã€ãã·ãŒã«é¢é£ããæ©èœã¯ãç¹å®ã®åœã§ã¯ç¡å¹ã«ããå¿ èŠãããå ŽåããããŸãã
4. ããã€ã¹ã®æ€åºãšã¢ãããã£ãã«ãŒãã£ã³ã°
ææ°ã®Webã¢ããªã±ãŒã·ã§ã³ã¯ãå¿çæ§ãé«ããããŸããŸãªç»é¢ãµã€ãºãšããã€ã¹ã®æ©èœã«é©å¿ããå¿ èŠããããŸããããã«ãŠã§ã¢ã䜿çšããŠããŠãŒã¶ãŒã®ããã€ã¹ã¿ã€ããæ€åºããæé©åãããããŒãžã§ã³ã®ãµã€ãã«ãªãã€ã¬ã¯ãã§ããŸãã
äŸïŒã¢ãã€ã«ãŠãŒã¶ãŒãã¢ãã€ã«åãã«æé©åããããµããã¡ã€ã³ã«ãªãã€ã¬ã¯ããã
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { device } from 'detection'
export function middleware(request: NextRequest) {
const userAgent = request.headers.get('user-agent')
if (userAgent) {
const deviceType = device(userAgent)
if (deviceType.type === 'phone') {
const mobileUrl = new URL(request.url)
mobileUrl.hostname = 'm.example.com'
return NextResponse.redirect(mobileUrl)
}
}
return NextResponse.next()
}
export const config = {
matcher: ['/'],
}
ãã®äŸã§ã¯ã`detection`ã©ã€ãã©ãªã䜿çšããŠãUser-Agent
ããããŒã«åºã¥ããŠãŠãŒã¶ãŒã®ããã€ã¹ã¿ã€ããå€å¥ããŸãããŠãŒã¶ãŒãæºåž¯é»è©±ã䜿çšããŠããå Žåãm.example.com
ãµããã¡ã€ã³ã«ãªãã€ã¬ã¯ããããŸãïŒã¢ãã€ã«åãã«æé©åãããããŒãžã§ã³ã®ãµã€ããããã«ãã¹ããããŠãããšä»®å®ããŸãïŒã `detection`ããã±ãŒãžãã€ã³ã¹ããŒã«ããããšãå¿ããªãã§ãã ããïŒ`npm install detection`ã
ã°ããŒãã«ãªèæ ®äºé ïŒããã€ã¹æ€åºããžãã¯ã§ãããã€ã¹ã®äœ¿çšã«ãããå°åããšã®ããªãšãŒã·ã§ã³ãèæ ®ãããŠããããšã確èªããŠãã ãããããšãã°ãäžéšã®éçºéäžåœã§ã¯ããã£ãŒãã£ãŒãã©ã³ããŸã æ®åããŠããŸããããå ç¢ãªãœãªã¥ãŒã·ã§ã³ã®ããã«ãUser-Agentæ€åºãšã¬ã¹ãã³ã·ããã¶ã€ã³æè¡ã®çµã¿åããã䜿çšããããšãæ€èšããŠãã ããã
5. ãªã¯ãšã¹ãããããŒã®æ¡åŒµ
ããã«ãŠã§ã¢ã¯ãã¢ããªã±ãŒã·ã§ã³ã«ãŒãã§åŠçãããåã«ããªã¯ãšã¹ãããããŒã«æ å ±ã远å ã§ããŸããããã¯ããŠãŒã¶ãŒããŒã«ãèªèšŒã¹ããŒã¿ã¹ããªã¯ãšã¹ãIDãªã©ã®ã«ã¹ã¿ã ã¡ã¿ããŒã¿ã远å ããå Žåã«åœ¹ç«ã¡ãŸãããããã®ã¡ã¿ããŒã¿ã¯ãã¢ããªã±ãŒã·ã§ã³ããžãã¯ã§äœ¿çšã§ããŸãã
äŸïŒãªã¯ãšã¹ãIDã®è¿œå
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { v4 as uuidv4 } from 'uuid'
export function middleware(request: NextRequest) {
const requestId = uuidv4()
const response = NextResponse.next()
response.headers.set('x-request-id', requestId)
return response
}
export const config = {
matcher: ['/api/:path*'], // Only apply to API routes
}
ãã®ããã«ãŠã§ã¢ã¯ãuuid
ã©ã€ãã©ãªã䜿çšããŠäžæã®ãªã¯ãšã¹ãIDãçæããx-request-id
ããããŒã«è¿œå ããŸãããã®IDã¯ããã®ã³ã°ããã¬ãŒã¹ãããã³ãããã°ã®ç®çã§äœ¿çšã§ããŸãã `uuid`ããã±ãŒãžãã€ã³ã¹ããŒã«ããããšãå¿ããªãã§ãã ããïŒ`npm install uuid`ã
ã°ããŒãã«ãªèæ ®äºé ïŒã«ã¹ã¿ã ããããŒã远å ããå Žåã¯ãããããŒãµã€ãºã®å¶éã«æ³šæããŠãã ããããããã®å¶éãè¶ ãããšãäºæããªããšã©ãŒãçºçããå¯èœæ§ããããŸãããŸããããããŒã«è¿œå ãããæ©å¯æ å ±ãé©åã«ä¿è·ãããŠããããšã確èªããŠãã ãããç¹ã«ãã¢ããªã±ãŒã·ã§ã³ããªããŒã¹ãããã·ãŸãã¯CDNã®èåŸã«ããå Žåã¯æ³šæãå¿ èŠã§ãã
6. ã»ãã¥ãªãã£ã®åŒ·åïŒã¬ãŒãå¶é
ããã«ãŠã§ã¢ã¯ãã¬ãŒãå¶éãå®è£ ããããšã«ãããæªæã®ããæ»æã«å¯Ÿããæåã®é²è¡ç·ãšããŠæ©èœã§ããŸããããã«ãããã¯ã©ã€ã¢ã³ããç¹å®ã®æéæ å ã«è¡ãããšãã§ãããªã¯ãšã¹ãã®æ°ãå¶éããããšã«ãããäžæ£äœ¿çšãé²ããŸãã
äŸïŒåçŽãªã¹ãã¢ã䜿çšããåºæ¬çãªã¬ãŒãå¶é
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const requestCounts: { [ip: string]: number } = {}
const WINDOW_SIZE_MS = 60000; // 1 minute
const MAX_REQUESTS_PER_WINDOW = 100;
export function middleware(request: NextRequest) {
const clientIP = request.ip || '127.0.0.1' // Get client IP, default to localhost for local testing
if (!requestCounts[clientIP]) {
requestCounts[clientIP] = 0;
}
requestCounts[clientIP]++;
if (requestCounts[clientIP] > MAX_REQUESTS_PER_WINDOW) {
return new NextResponse(
JSON.stringify({ message: 'Too many requests' }),
{ status: 429, headers: { 'Content-Type': 'application/json' } }
);
}
// Reset count after window
setTimeout(() => {
requestCounts[clientIP]--;
if (requestCounts[clientIP] <= 0) {
delete requestCounts[clientIP];
}
}, WINDOW_SIZE_MS);
return NextResponse.next();
}
export const config = {
matcher: ['/api/:path*'], // Apply to all API routes
}
ãã®äŸã§ã¯ãåIPã¢ãã¬ã¹ããã®ãªã¯ãšã¹ãæ°ã远跡ããããã«ãåçŽãªã€ã³ã¡ã¢ãªã¹ãã¢ïŒrequestCounts
ïŒãä¿æããŸããã¯ã©ã€ã¢ã³ããWINDOW_SIZE_MS
å
ã®MAX_REQUESTS_PER_WINDOW
ãè¶
ããå Žåãããã«ãŠã§ã¢ã¯429 Too Many Requests
ãšã©ãŒãè¿ããŸãã éèŠïŒããã¯ç°¡ç¥åãããäŸã§ãããã¹ã±ãŒãªã³ã°ã§ããããµãŒãã¹æåŠæ»æã«å¯ŸããŠè匱ã§ãããããæ¬çªç°å¢ã«ã¯é©ããŠããŸãããæ¬çªç°å¢ã§äœ¿çšããå Žåã¯ãRedisãªã©ã®ããå
ç¢ãªã¬ãŒãå¶éãœãªã¥ãŒã·ã§ã³ããŸãã¯å°çšã®ã¬ãŒãå¶éãµãŒãã¹ã®äœ¿çšãæ€èšããŠãã ããã
ã°ããŒãã«ãªèæ ®äºé ïŒã¬ãŒãå¶éæŠç¥ã¯ãã¢ããªã±ãŒã·ã§ã³ã®ç¹å®ã®ç¹æ§ãšãŠãŒã¶ãŒã®å°ççãªååžã«åãããŠèª¿æŽããå¿ èŠããããŸããå°åãŸãã¯ãŠãŒã¶ãŒã»ã°ã¡ã³ãããšã«ç°ãªãã¬ãŒãå¶éã䜿çšããããšãæ€èšããŠãã ããã
ãšããžã±ãŒã¹ãšæœåšçãªèœãšã穎
ããã«ãŠã§ã¢ã¯åŒ·åãªããŒã«ã§ããããã®å¶éãšæœåšçãªèœãšãç©Žã«æ³šæããããšãäžå¯æ¬ ã§ãã
- ããã©ãŒãã³ã¹ãžã®åœ±é¿ïŒããã«ãŠã§ã¢ã¯ãã¹ãŠã®ãªã¯ãšã¹ãã«ãªãŒããŒãããã远å ããŸããããã«ãŠã§ã¢ã§èšç®ã³ã¹ãã®é«ãæäœãå®è¡ããããšã¯é¿ããŠãã ãããããã¯ããã©ãŒãã³ã¹ã«å€§ããªåœ±é¿ãäžããå¯èœæ§ããããŸããããã«ãŠã§ã¢ããããã¡ã€ã«ããŠãããã©ãŒãã³ã¹ã®ããã«ããã¯ãç¹å®ããŠæé©åããŸãã
- è€éãïŒããã«ãŠã§ã¢ãé床ã«äœ¿çšãããšãã¢ããªã±ãŒã·ã§ã³ã®çè§£ãšä¿å®ãå°é£ã«ãªãå¯èœæ§ããããŸããããã«ãŠã§ã¢ã¯æ éã«äœ¿çšããåããã«ãŠã§ã¢é¢æ°ã«æç¢ºã«å®çŸ©ãããç®çãããããšã確èªããŠãã ããã
- ãã¹ãïŒããã«ãŠã§ã¢ã®ãã¹ãã¯ãHTTPãªã¯ãšã¹ããã·ãã¥ã¬ãŒãããçµæã®å¿çãæ€æ»ããå¿ èŠããããããå°é£ãªå ŽåããããŸãã JestãSupertestãªã©ã®ããŒã«ã䜿çšããŠãããã«ãŠã§ã¢é¢æ°ã®å æ¬çãªãŠããããã¹ããšçµ±åãã¹ããäœæããŸãã
- Cookieã®ç®¡çïŒããã«ãŠã§ã¢ã§Cookieãèšå®ããå Žåã¯ããã£ãã·ã¥ã®åäœã«åœ±é¿ãäžããå¯èœæ§ãããããæ³šæããŠãã ããã CookieããŒã¹ã®ãã£ãã·ã¥ã®åœ±é¿ãçè§£ããããã«å¿ããŠãã£ãã·ã¥ããããŒãæ§æããŠãã ããã
- ç°å¢å€æ°ïŒããã«ãŠã§ã¢ã§äœ¿çšããããã¹ãŠã®ç°å¢å€æ°ããããŸããŸãªç°å¢ïŒéçºãã¹ããŒãžã³ã°ãæ¬çªïŒã«å¯ŸããŠé©åã«æ§æãããŠããããšã確èªããŠãã ããã Dotenvãªã©ã®ããŒã«ã䜿çšããŠãç°å¢å€æ°ã管çããŸãã
- ãšããžé¢æ°ã®å¶éïŒããã«ãŠã§ã¢ã¯ãšããžé¢æ°ãšããŠå®è¡ããããããå®è¡æéãã¡ã¢ãªäœ¿çšéãããã³ãã³ãã«ãããã³ãŒããµã€ãºã«å¶éãããããšã«æ³šæããŠãã ãããããã«ãŠã§ã¢é¢æ°ã軜éãã€å¹ççã«ä¿ã¡ãŸãã
Next.jsããã«ãŠã§ã¢ã䜿çšããããã®ãã¹ããã©ã¯ãã£ã¹
Next.jsããã«ãŠã§ã¢ã®ã¡ãªãããæå€§åããæœåšçãªåé¡ãåé¿ããã«ã¯ã次ã®ãã¹ããã©ã¯ãã£ã¹ã«åŸã£ãŠãã ããã
- ã·ã³ãã«ã«ä¿ã€ïŒåããã«ãŠã§ã¢é¢æ°ã¯ãåäžã®æç¢ºã«å®çŸ©ããã責任ãæã€å¿ èŠããããŸããè€æ°ã®ã¿ã¹ã¯ãå®è¡ããé床ã«è€éãªããã«ãŠã§ã¢é¢æ°ãäœæããããšã¯é¿ããŠãã ããã
- ããã©ãŒãã³ã¹ã®æé©åïŒããã©ãŒãã³ã¹ã®ããã«ããã¯ãåé¿ããããã«ãããã«ãŠã§ã¢ã§å®è¡ãããåŠçéãæå°éã«æããŸãããã£ãã·ã¥æŠç¥ã䜿çšããŠãç¹°ãè¿ãã®èšç®ã®å¿ èŠæ§ãæžãããŸãã
- 培åºçãªãã¹ãïŒããã«ãŠã§ã¢é¢æ°ã®å æ¬çãªãŠããããã¹ããšçµ±åãã¹ããäœæããŠãæåŸ ã©ããã«åäœããããšã確èªããŸãã
- ã³ãŒãã®ããã¥ã¡ã³ãåïŒä¿å®æ§ãåäžãããããã«ãåããã«ãŠã§ã¢é¢æ°ã®ç®çãšæ©èœãæç¢ºã«ããã¥ã¡ã³ãåããŸãã
- ã¢ããªã±ãŒã·ã§ã³ã®ç£èŠïŒç£èŠããŒã«ã䜿çšããŠãããã«ãŠã§ã¢é¢æ°ã®ããã©ãŒãã³ã¹ãšãšã©ãŒçã远跡ããŸãã
- å®è¡é åºã®çè§£ïŒããã«ãŠã§ã¢é¢æ°ã®å®è¡é åºã«æ³šæããŠãã ãããããã¯ããã®åäœã«åœ±é¿ãäžããå¯èœæ§ããããŸãã
- ç°å¢å€æ°ã®è³¢æãªäœ¿çšïŒç°å¢å€æ°ã䜿çšããŠãããŸããŸãªç°å¢ã«åãããŠããã«ãŠã§ã¢é¢æ°ãæ§æããŸãã
çµè«
Next.jsããã«ãŠã§ã¢ã¯ããªã¯ãšã¹ãã倿Žãããšããžã§ã¢ããªã±ãŒã·ã§ã³ã®åäœãã«ã¹ã¿ãã€ãºããããã®åŒ·åãªæ¹æ³ãæäŸããŸãããã®ã¬ã€ãã§èª¬æãããŠããé«åºŠãªãªã¯ãšã¹ã倿Žãã¿ãŒã³ãçè§£ããããšã§ãå ç¢ã§ããã©ãŒãã³ã¹ãé«ããã°ããŒãã«ã«å¯Ÿå¿ã§ããNext.jsã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ã§ããŸãããšããžã±ãŒã¹ãšæœåšçãªèœãšãç©Žãæ éã«æ€èšããäžèšã®ã¢ãŠãã©ã€ã³ã§ç€ºããããã¹ããã©ã¯ãã£ã¹ã«åŸã£ãŠãããã«ãŠã§ã¢é¢æ°ãä¿¡é Œæ§ãé«ãä¿å®å¯èœã§ããããšã確èªããŠãã ãããããã«ãŠã§ã¢ã®åãæŽ»çšããŠãåè¶ãããŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ãäœæããWebã¢ããªã±ãŒã·ã§ã³ã®æ°ããå¯èœæ§ãè§£ãæŸã¡ãŸãããã